package cn.edu.hqu.pre;

import cn.hutool.core.util.NumberUtil;
import cn.hutool.poi.excel.ExcelReader;
import cn.hutool.poi.excel.ExcelUtil;
import org.uma.jmetal.algorithm.Algorithm;
import org.uma.jmetal.algorithm.examples.AlgorithmRunner;
import org.uma.jmetal.algorithm.multiobjective.nsgaii.NSGAIIBuilder;
import org.uma.jmetal.operator.crossover.CrossoverOperator;
import org.uma.jmetal.operator.crossover.impl.PMXCrossover;
import org.uma.jmetal.operator.mutation.MutationOperator;
import org.uma.jmetal.operator.mutation.impl.PermutationSwapMutation;
import org.uma.jmetal.operator.selection.SelectionOperator;
import org.uma.jmetal.operator.selection.impl.BinaryTournamentSelection;
import org.uma.jmetal.solution.permutationsolution.PermutationSolution;
import org.uma.jmetal.util.JMetalLogger;
import org.uma.jmetal.util.comparator.RankingAndCrowdingDistanceComparator;
import org.uma.jmetal.util.fileoutput.SolutionListOutput;
import org.uma.jmetal.util.fileoutput.impl.DefaultFileOutputContext;
import org.uma.jmetal.util.pseudorandom.JMetalRandom;

import java.util.List;
import java.util.Map;

/**
 * 基于JMetal求解“宗教文化体验旅游线”
 *
 * @author 洪佳森
 * @date 2023-12-11
 */
public class ReligiousCulturalExperienceTourismRouteMain {

    public static void main(String[] args) {
        ReligiousCulturalExperienceTourismRouteProblem problem
                = new ReligiousCulturalExperienceTourismRouteProblem();

        problem.setDistanceMatrix(readData1());
        problem.setTravelTimeMatrix(readData2());
        problem.setPlayMatrix(readData3());

        // 构建交叉概率0.9的PMX交换算子
        CrossoverOperator<PermutationSolution<Integer>> crossover = new PMXCrossover(0.9);
        // 构建变异概率0.2的排列交换变异算子
        double mutationProbability = 0.2;
        MutationOperator<PermutationSolution<Integer>> mutation = new PermutationSwapMutation<Integer>(
                mutationProbability);
        // 构建锦标赛选择算子
        SelectionOperator<List<PermutationSolution<Integer>>, PermutationSolution<Integer>> selection =
                new BinaryTournamentSelection<>(new RankingAndCrowdingDistanceComparator<>());
        // 种群规模100
        int populationSize = 100;

        // 构建算法，最大迭代次数50000
        Algorithm<List<PermutationSolution<Integer>>> algorithm =
                new NSGAIIBuilder<>(
                        problem, crossover, mutation, populationSize)
                        .setSelectionOperator(selection)
                        .setMaxEvaluations(50000)
                        .build();

        AlgorithmRunner algorithmRunner = new AlgorithmRunner.Executor(algorithm).execute();

        List<PermutationSolution<Integer>> population = algorithm.result();
        long computingTime = algorithmRunner.getComputingTime();

        new SolutionListOutput(population)
                .setVarFileOutputContext(new DefaultFileOutputContext(problem.name() + "VAR.tsv"))
                .setFunFileOutputContext(new DefaultFileOutputContext(problem.name() + "FUN.tsv"))
                .print();

        JMetalLogger.logger.info("Total execution time: " + computingTime + "ms");
        JMetalLogger.logger.info("Random seed: " + JMetalRandom.getInstance().getSeed());
        JMetalLogger.logger.info("Objectives values have been written to file " + problem.name() + "FUN.tsv");
        JMetalLogger.logger.info("Variables values have been written to file " + problem.name() + "VAR.tsv");
    }

    public static double[][] readData1() {
        double [][] matrix = new double[8][8];

        ExcelReader reader =  ExcelUtil.getReader("F:\\test-project\\hqu-route-design\\src\\main\\resources\\data.xlsx", 0);
        List<Map<String, Object>> data = reader.read(0, 1, reader.getRowCount());

        for (int i = 0; i < data.size(); i++) {
            String xs = data.get(i).get("遗产点i").toString();
            String ys = data.get(i).get("遗产点j").toString();
            if (Code2Help.codeMap.containsKey(xs) && Code2Help.codeMap.containsKey(ys)) {
                double d = NumberUtil.parseDouble(data.get(i).get("最快的路程（千米）").toString());

                Integer x = Code2Help.codeMap.get(xs);
                Integer y = Code2Help.codeMap.get(ys);
                matrix[x][y] = d;
                matrix[y][x] = d;
            }
        }

        return matrix;
    }

    public static double[][] readData2() {
        double [][] matrix = new double[8][8];

        ExcelReader reader =  ExcelUtil.getReader("F:\\test-project\\hqu-route-design\\src\\main\\resources\\data.xlsx", 0);
        List<Map<String, Object>> data = reader.read(0, 1, reader.getRowCount());

        for (int i = 0; i < data.size(); i++) {
            String xs = data.get(i).get("遗产点i").toString();
            String ys = data.get(i).get("遗产点j").toString();
            if (Code2Help.codeMap.containsKey(xs) && Code2Help.codeMap.containsKey(ys)) {
                double d = NumberUtil.parseDouble(data.get(i).get("旅途时间（分钟）").toString());

                Integer x = Code2Help.codeMap.get(xs);
                Integer y = Code2Help.codeMap.get(ys);
                matrix[x][y] = d;
                matrix[y][x] = d;
            }
        }

        return matrix;
    }

    public static double[] readData3() {
        double [] matrix = new double[8];

        ExcelReader reader =  ExcelUtil.getReader("F:\\test-project\\hqu-route-design\\src\\main\\resources\\data.xlsx", 1);
        List<Map<String, Object>> data = reader.read(0, 1, reader.getRowCount());

        for (int i = 0; i < data.size(); i++) {
            String xs = data.get(i).get("遗产点").toString();
            if (Code2Help.codeMap.containsKey(xs)) {
                double d = NumberUtil.parseDouble(data.get(i).get("推荐游玩时间").toString());

                Integer x = Code2Help.codeMap.get(xs);
                matrix[x] = d;
            }
        }

        return matrix;
    }
}
